"""
页式存储系统 V3.0
完全重新设计，实现数据持久化
"""

import os
import struct
import json
from typing import Dict, Any, List, Optional, Tuple
from collections import OrderedDict

class PageStorageV3:
    """页式存储系统 V3.0"""
    
    def __init__(self, data_dir: str = "data"):
        """初始化页式存储系统"""
        self.data_dir = data_dir
        self.page_size = 4096  # 4KB页面大小
        self.cache_size = 100  # 缓存页面数量
        self.page_cache = OrderedDict()  # LRU缓存
        self.page_info_file = os.path.join(data_dir, "page_info.json")
        
        # 确保数据目录存在
        os.makedirs(data_dir, exist_ok=True)
        
        # 加载页面信息
        self.page_info = self._load_page_info()
    
    def _load_page_info(self) -> Dict[str, Any]:
        """加载页面信息"""
        default_info = {
            'next_page_id': 0,
            'free_pages': [],
            'allocated_pages': {},
            'table_pages': {}
        }
        
        if os.path.exists(self.page_info_file):
            try:
                with open(self.page_info_file, 'r', encoding='utf-8') as f:
                    loaded_info = json.load(f)
                    # 确保所有必需的键都存在，并且类型正确
                    for key, default_value in default_info.items():
                        if key not in loaded_info:
                            loaded_info[key] = default_value
                        elif key == 'allocated_pages' and not isinstance(loaded_info[key], dict):
                            loaded_info[key] = {}
                        elif key == 'table_pages' and not isinstance(loaded_info[key], dict):
                            loaded_info[key] = {}
                        elif key == 'free_pages' and not isinstance(loaded_info[key], list):
                            loaded_info[key] = []
                    return loaded_info
            except:
                pass
        
        return default_info
    
    def _save_page_info(self):
        """保存页面信息"""
        try:
            with open(self.page_info_file, 'w', encoding='utf-8') as f:
                json.dump(self.page_info, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存页面信息失败: {e}")
    
    def allocate_page(self) -> int:
        """分配新页面"""
        # 首先尝试使用空闲页面
        if self.page_info['free_pages']:
            page_id = self.page_info['free_pages'].pop(0)
        else:
            # 分配新页面
            page_id = self.page_info['next_page_id']
            self.page_info['next_page_id'] += 1
        
        # 标记页面为已分配
        self.page_info['allocated_pages'][str(page_id)] = {
            'allocated_at': self._get_current_time(),
            'dirty': False
        }
        
        # 创建页面文件
        page_file = self._get_page_file(page_id)
        if not os.path.exists(page_file):
            self._create_empty_page(page_id)
        
        self._save_page_info()
        return page_id
    
    def deallocate_page(self, page_id: int):
        """释放页面"""
        page_id_str = str(page_id)
        
        if page_id_str in self.page_info['allocated_pages']:
            # 从已分配列表中移除
            del self.page_info['allocated_pages'][page_id_str]
            
            # 添加到空闲列表
            self.page_info['free_pages'].append(page_id)
            
            # 从缓存中移除
            if page_id in self.page_cache:
                del self.page_cache[page_id]
            
            # 删除页面文件
            page_file = self._get_page_file(page_id)
            if os.path.exists(page_file):
                os.remove(page_file)
            
            self._save_page_info()
    
    def read_page(self, page_id: int) -> bytes:
        """读取页面数据"""
        # 检查缓存
        if page_id in self.page_cache:
            # 移动到末尾（LRU）
            data = self.page_cache.pop(page_id)
            self.page_cache[page_id] = data
            return data
        
        # 从磁盘读取
        page_file = self._get_page_file(page_id)
        if not os.path.exists(page_file):
            raise FileNotFoundError(f"页面 {page_id} 不存在")
        
        try:
            with open(page_file, 'rb') as f:
                data = f.read()
            
            # 添加到缓存
            self._add_to_cache(page_id, data)
            
            return data
        except Exception as e:
            raise IOError(f"读取页面 {page_id} 失败: {e}")
    
    def write_page(self, page_id: int, data: bytes):
        """写入页面数据"""
        if len(data) > self.page_size:
            raise ValueError(f"页面数据大小 ({len(data)}) 超过页面大小 ({self.page_size})")
        
        # 确保数据大小正确
        if len(data) < self.page_size:
            data = data + b'\x00' * (self.page_size - len(data))
        
        # 写入磁盘
        page_file = self._get_page_file(page_id)
        try:
            with open(page_file, 'wb') as f:
                f.write(data)
            
            # 更新缓存
            self._add_to_cache(page_id, data)
            
            # 标记为脏页
            if str(page_id) in self.page_info['allocated_pages']:
                self.page_info['allocated_pages'][str(page_id)]['dirty'] = True
            
        except Exception as e:
            raise IOError(f"写入页面 {page_id} 失败: {e}")
    
    def _add_to_cache(self, page_id: int, data: bytes):
        """添加页面到缓存"""
        # 如果缓存已满，移除最旧的页面
        if len(self.page_cache) >= self.cache_size:
            oldest_page_id, _ = self.page_cache.popitem(last=False)
        
        self.page_cache[page_id] = data
    
    def _get_page_file(self, page_id: int) -> str:
        """获取页面文件路径"""
        return os.path.join(self.data_dir, f"page_{page_id:06d}.dat")
    
    def _create_empty_page(self, page_id: int):
        """创建空页面"""
        page_file = self._get_page_file(page_id)
        with open(page_file, 'wb') as f:
            f.write(b'\x00' * self.page_size)
    
    def _get_current_time(self) -> str:
        """获取当前时间字符串"""
        import time
        return str(int(time.time()))
    
    def get_page_info(self) -> Dict[str, Any]:
        """获取页面信息统计"""
        return {
            'total_pages': self.page_info['next_page_id'],
            'allocated_pages': len(self.page_info['allocated_pages']),
            'free_pages': len(self.page_info['free_pages']),
            'cache_size': len(self.page_cache),
            'page_size': self.page_size
        }
    
    def flush_cache(self):
        """刷新缓存"""
        self.page_cache.clear()
    
    def assign_pages_to_table(self, table_name: str, page_ids: List[int]):
        """为表分配页面"""
        self.page_info['table_pages'][table_name] = page_ids
        self._save_page_info()
    
    def get_table_pages(self, table_name: str) -> List[int]:
        """获取表的页面列表"""
        return self.page_info['table_pages'].get(table_name, [])
    
    def add_page_to_table(self, table_name: str, page_id: int):
        """为表添加页面"""
        if table_name not in self.page_info['table_pages']:
            self.page_info['table_pages'][table_name] = []
        
        if page_id not in self.page_info['table_pages'][table_name]:
            self.page_info['table_pages'][table_name].append(page_id)
            self._save_page_info()
    
    def remove_table_pages(self, table_name: str):
        """移除表的所有页面"""
        if table_name in self.page_info['table_pages']:
            page_ids = self.page_info['table_pages'][table_name]
            for page_id in page_ids:
                self.deallocate_page(page_id)
            del self.page_info['table_pages'][table_name]
            self._save_page_info()

class RecordSerializer:
    """记录序列化器"""
    
    @staticmethod
    def serialize_record(record: Dict[str, Any], schema: Dict[str, Any]) -> bytes:
        """序列化记录"""
        data = bytearray()
        
        for col_name, col_info in schema['columns'].items():
            value = record.get(col_name)
            col_type = col_info['type']['type']
            
            if value is None:
                # NULL值用特殊标记
                data.extend(b'\xFF\xFF\xFF\xFF')
            elif col_type in ['int', 'integer']:
                data.extend(struct.pack('<i', int(value)))
            elif col_type in ['float', 'double']:
                data.extend(struct.pack('<d', float(value)))
            elif col_type in ['varchar', 'char']:
                str_value = str(value)
                str_bytes = str_value.encode('utf-8')
                # 长度前缀 + 数据
                data.extend(struct.pack('<I', len(str_bytes)))
                data.extend(str_bytes)
            elif col_type in ['boolean', 'bool']:
                data.extend(struct.pack('<?', bool(value)))
            else:
                # 默认作为字符串处理
                str_value = str(value)
                str_bytes = str_value.encode('utf-8')
                data.extend(struct.pack('<I', len(str_bytes)))
                data.extend(str_bytes)
        
        return bytes(data)
    
    @staticmethod
    def deserialize_record(data: bytes, schema: Dict[str, Any]) -> Dict[str, Any]:
        """反序列化记录"""
        record = {}
        offset = 0
        
        for col_name, col_info in schema['columns'].items():
            col_type = col_info['type']['type']
            
            if col_type in ['int', 'integer']:
                if offset + 4 > len(data):
                    break
                value = struct.unpack('<i', data[offset:offset+4])[0]
                offset += 4
            elif col_type in ['float', 'double']:
                if offset + 8 > len(data):
                    break
                value = struct.unpack('<d', data[offset:offset+8])[0]
                offset += 8
            elif col_type in ['varchar', 'char']:
                if offset + 4 > len(data):
                    break
                length = struct.unpack('<I', data[offset:offset+4])[0]
                offset += 4
                if offset + length > len(data):
                    break
                value = data[offset:offset+length].decode('utf-8')
                offset += length
            elif col_type in ['boolean', 'bool']:
                if offset + 1 > len(data):
                    break
                value = struct.unpack('<?', data[offset:offset+1])[0]
                offset += 1
            else:
                # 默认作为字符串处理
                if offset + 4 > len(data):
                    break
                length = struct.unpack('<I', data[offset:offset+4])[0]
                offset += 4
                if offset + length > len(data):
                    break
                value = data[offset:offset+length].decode('utf-8')
                offset += length
            
            # 检查是否为NULL值
            if col_type in ['int', 'integer'] and value == 0xFFFFFFFF:
                value = None
            elif col_type in ['float', 'double'] and value == float('inf'):
                value = None
            
            record[col_name] = value
        
        return record

def main():
    """测试页式存储系统"""
    storage = PageStorageV3("test_data")
    
    # 测试页面分配
    print("测试页面分配...")
    page_id = storage.allocate_page()
    print(f"分配页面: {page_id}")
    
    # 测试数据写入
    test_data = b"Hello, World!" + b'\x00' * (4096 - 13)
    storage.write_page(page_id, test_data)
    print("写入测试数据")
    
    # 测试数据读取
    read_data = storage.read_page(page_id)
    print(f"读取数据: {read_data[:13].decode('utf-8')}")
    
    # 测试页面信息
    info = storage.get_page_info()
    print(f"页面信息: {info}")
    
    # 测试记录序列化
    schema = {
        'columns': {
            'id': {'type': {'type': 'int'}},
            'name': {'type': {'type': 'varchar'}},
            'age': {'type': {'type': 'int'}}
        }
    }
    
    record = {'id': 1, 'name': 'Alice', 'age': 25}
    serialized = RecordSerializer.serialize_record(record, schema)
    print(f"序列化记录: {len(serialized)} 字节")
    
    deserialized = RecordSerializer.deserialize_record(serialized, schema)
    print(f"反序列化记录: {deserialized}")
    
    # 清理
    storage.deallocate_page(page_id)
    print("页面已释放")

if __name__ == "__main__":
    main()